// Package service
// Created by GoLand
// @User: lenora
// @Date: 2023/11/29
// @Time: 18:08

package service

import (
	"camp/microbook/internal/domain"
	"camp/microbook/internal/repository"
	"context"
	"golang.org/x/crypto/bcrypt"
)

type UserService interface {
	Edit(ctx context.Context, uid uint64, nickname, birth, desc string) error
	FindOrCreate(ctx context.Context, phone string) (domain.UserBase, error)
	Login(ctx context.Context, userName, password string) (domain.UserBase, error)
	Profile(ctx context.Context, uid uint64) (domain.User, error)
	Signup(ctx context.Context, u domain.UserBase) error
	WechatFindOrCreate(ctx context.Context, openid string) (domain.UserBase, error)
}

type userService struct {
	repo repository.UserRepository
}

func NewUserService(repo repository.UserRepository) UserService {
	return &userService{
		repo: repo,
	}
}

func (srv *userService) Signup(ctx context.Context, u domain.UserBase) error {
	pwd, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.Password = string(pwd)
	return srv.repo.Create(ctx, u)
}

func (srv *userService) Login(ctx context.Context, userName, password string) (domain.UserBase, error) {
	u, err := srv.repo.FindByName(ctx, userName)
	if err == repository.ErrUserNotFound {
		return domain.UserBase{}, ErrInvalidUser
	}

	if err != nil {
		return domain.UserBase{}, err
	}
	//验证密码
	if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)); err != nil {
		return domain.UserBase{}, ErrInvalidUser
	}

	return u, nil
}

func (srv *userService) Profile(ctx context.Context, uid uint64) (domain.User, error) {
	u, err := srv.repo.FindById(ctx, uid)
	if err == repository.ErrUserNotFound {
		return domain.User{}, ErrUserNotFound
	}

	if err != nil {
		return domain.User{}, err
	}
	return u, nil
}

func (srv *userService) Edit(ctx context.Context, uid uint64, nickname, birth, desc string) error {
	_, err := srv.repo.FindById(ctx, uid)
	if err == repository.ErrUserNotFound {
		return ErrUserNotFound
	}

	if err != nil {
		return err
	}
	return srv.repo.Edit(ctx, domain.User{
		Id:       uid,
		Nickname: nickname,
		Birth:    birth,
		Desc:     desc,
	})
}

func (srv *userService) FindOrCreate(ctx context.Context, phone string) (domain.UserBase, error) {
	u, err := srv.repo.FindByPhone(ctx, phone)
	if err != repository.ErrUserNotFound {
		// 1.err=nil,用户已存在
		// 2.err != nil,查询出错 系统错误
		return u, err
	}
	err = srv.repo.Create(ctx, domain.UserBase{
		Phone: phone,
	})
	if err != nil && err != repository.ErrDuplicateUser {
		return domain.UserBase{}, err
	}
	//这个时候要么就是用户已存在  要么就是err=nil
	//NOTE:如果存在多库的情况下,可能存在主从库延迟的问题(理论上来讲,可以强制走主库)
	return srv.repo.FindByPhone(ctx, phone)
}

func (srv *userService) WechatFindOrCreate(ctx context.Context, openid string) (domain.UserBase, error) {
	u, err := srv.repo.FindByOpenid(ctx, openid)
	if err != repository.ErrUserNotFound {
		// 1.err=nil,用户已存在
		// 2.err != nil,查询出错 系统错误
		return u, err
	}
	err = srv.repo.Create(ctx, domain.UserBase{
		Openid: openid,
	})
	if err != nil && err != repository.ErrDuplicateUser {
		return domain.UserBase{}, err
	}
	//这个时候要么就是用户已存在  要么就是err=nil
	//NOTE:如果存在多库的情况下,可能存在主从库延迟的问题(理论上来讲,可以强制走主库)
	return srv.repo.FindByOpenid(ctx, openid)

}
